<?php
/**
 * Game_PlaceBuilding
 *
 * Класс Game_PlaceBuilding обрабатывает данные, связанные со зданием в месте
 *
 * @author Дмитрий Кузьма <kouzma.d@gmail.com>
 * @version 1.0
 */
class Game_PlaceBuilding extends MultiKeyRecord {
	protected static $_tableName = 'place_building';
	protected static $_arrPkNames = array(
		'ID_place' => 'placeId',
		'ID_position' => 'positionId',
	);
	public $placeId = null;
	public $positionId = null;
	
	public $objBuilding;

	/**
	 * Конструктор
	 * 
	 */
	public function __construct() {
    Service_Debug::setDebug();
    $args = func_get_args();
    if (isset($args[1]))
    parent::__construct($args[0], $args[1]);
    // Если загрузился объект
    if (!empty($this->placeId)) {
    	$this->objBuilding = new Game_Building($this->getField('ID_building'));
    }
	}  
	/**
	 * Создает изначальные здания в месте
	 * 
	 * @param Game_Place $objPlace - место, в котором нужно отстроить здания
	 */
	public static function createPlaceStartBuildings(Game_Place $objPlace) {
		// Заполняем, сколько каких зданий какого уровня нужно сразу построить
		$arrToCreate = array(
				1 => array(6, 0),
				2 => array(4, 0),
				3 => array(4, 0),
				4 => array(4, 0),
				5 => array(1, 1)
		);
		$i = 1;
		// Строим согласно подготовленному массиву
		// $i используется в качестве позиции для постройки
		foreach ($arrToCreate as $buildingId => $arrBuildingsInfo) {
			list($buildingsAmount, $level) = $arrBuildingsInfo;
			for ($sortNum = 0; $sortNum < $buildingsAmount; $sortNum++) {
				$objPlaceBuilding = new Game_PlaceBuilding($objPlace->id, $i++);
				$objPlaceBuilding->setField('level', $level);
				$objPlaceBuilding->setField('ID_building', $buildingId);
				$objPlaceBuilding->save();
			}
		}
	}
	
	/**
	 * Метод проверяет, возможно ли улучшать здание и дальше
	 * 
	 */
	public function isHasNextLevelToUpgrade() {
		if ($this->getField('level') < 20) return true;
	}
	
	/**
	 * Метод проверяет, возможно ли улучшить здание прямо сейчас
	 * 
	 * @param bool $needToThrowExceptions - флаг, выбрасывать ли исключения, если невозможно сделать апгрейд
	 * 
	 * @throws Exception_Game
	 */
	public function isAvailableToUpgrade($needToThrowExceptions = false) {
		// Проверяем, можно ли у здания произвести апгрейд до соответсвующего уровня
		$level = $this->getLevelToBuild();
		if (!$level) {
			if ($needToThrowExceptions) throw new Exception_Game_Level(Service_Translate::getText('Здание достигло максимального уровня'));
			else return false;
		}
		
		// Проверяем, не строится ли в данный момент это здание
		if (Game_Event_Building::isPlaceEventExist($this->positionId)) {
			if ($needToThrowExceptions) throw new Exception_Game_CurrentAction(Service_Translate::getText('Здание в данный момент строится'));
			else return false;
		}
		
		$objGeneral = Game_General::instance();
		// Проверяем, не строится ли в данный момент что-то другое в месте
		if ($objGeneral->objPlace->hasBuildingEvents()) {
			if ($needToThrowExceptions) throw new Exception_Game_BusyWorkers(Service_Translate::getText('В данный момент проходит другое строительство'));
			else return false;
		}
		
		// Проверяет, хватает ли ресурсов для апгрейда
		if (!$this->_isEnoughResources($level)) {
			if ($needToThrowExceptions) throw new Exception_Game_Resources(); // Не хватает ресурсов
			else return false;
		}
		
		return true;
	}
	
	/**
	 * Метод проверяет, возможно ли построить здание прямо сейчас
	 * 
 	 * @param bool $needToThrowExceptions - флаг, выбрасывать ли исключения, если невозможно сделать апгрейд
	 * 
	 * @throws Exception_Game
	 */
	public function isAvailableToCreate($needToThrowExceptions = false) {
		// Проверяем зависимости от зданий
		if (!$this->_isAvailableToCreateByAnotherBuildings()) {
			if ($needToThrowExceptions) throw new Exception_Game_BuildingsDependences();
			else return false;
		}
		
		$objGeneral = Game_General::instance();
		if ($objGeneral->objPlace->getBuildingIdByPosition($this->positionId)) {
			if ($needToThrowExceptions) throw new Exception_Game_Exist(Service_Translate::getText('В этом месте уже построено другое здание'));
			else return false;
		}
		
		// Проверяем, не строится ли в данный момент
		if (Game_Event_Building::isPlaceEventExist($this->positionId)) {
			if ($needToThrowExceptions) throw new Exception_Game_CurrentAction(Service_Translate::getText('Здание в данный момент строится'));
		}
		
		// Проверяем, не строится ли в данный момент что-то другое в месте
		if ($objGeneral->objPlace->hasBuildingEvents()) {
			if ($needToThrowExceptions) throw new Exception_Game_BusyWorkers(Service_Translate::getText('В данный момент проходит другое строительство'));
			else return false;
		}
		
		// Проверяем зависимости от ресурсов
		if (!$this->_isEnoughResources(1)) {
			if ($needToThrowExceptions) throw new Exception_Game_Resources(); // Не хватает ресурсов
			else return false;
		}

		return true;
	}
	
	/**
	 * Проверяет, возможно ли строительство здания - проверяет, позволяют ли остальные здания постройку.
	 */
	private function _isAvailableToCreateByAnotherBuildings() {
		// TODO - написать метод
		$objGeneral = Game_General::instance();
		$arrDependences = Game_Building::getBuildingsDependencesByIds($this->objBuilding->id);
		$arrPlaceBuildings = $objGeneral->objPlace->getBuildingsData();
		$arrNecessaryBildingsRet = $this->objBuilding->getNecessaryBuildingsToBuildInPlace($arrPlaceBuildings, $arrDependences);
		return empty($arrNecessaryBildingsRet);
	}
	
	/**
	 * Проверяет, достаточное ли население для строительства
	 * 
	 */
	protected function _isEnoughPopulation() {
		// TODO проверка по населению
		return true;
	}
	
	/**
	 * Проверяет, достаточное ли ресурсов для строительства
	 * 
	 * @param int $level
	 */
	protected function _isEnoughResources($level) {
		$objResource = $this->objBuilding->getBuildingLevelUpgradeCost($level);
		$objGeneral = Game_General::instance();
		return $objGeneral->objPlace->objResourcesWithProduction->isEnoughResources($objResource);
	}
	
	/**
	 * Подсчитывает, через какое кол-во секунд будет достаточно ресурсов
	 * 
	 * @param int $level
	 */
	public function calculateSecondToEnoughResources($level) {
		$objResource = $this->objBuilding->getBuildingLevelUpgradeCost($level);
		$objGeneral = Game_General::instance();
		return $objGeneral->objPlace->objResourcesWithProduction->calculateSecondsToEnoughResources($objResource);
	}
	
	/**
	 * Проверяет, достаточное ли ресурсов для строительства, если их поменять
	 * 
	 */
	protected function _isEnoughResourcesIfConvert() {
		// TODO проверка по ресурсам при конвертации
		return true;
	}
	
	
	/**
	 * Проверяет, есть ли места в очереди на строительство
	 * 
	 */
	protected function _isAvailableQueueToBuild() {
		// TODO проверка доступности очереди
		return true;
	}

	/**
	 * Получает следующий уровень строительства здания
	 * 
	 * @param Game_Building $objBuilding
	 */
	public function getLevelToBuild() {
		if ($this->isHasNextLevelToUpgrade()) return $this->getField('level') + 1;
		else return false;
	}
	
	/**
	 * Возвращает $url строения ID_position
	 * 
	 * @param int $positionId
	 */
	public static function getPlaceBuildingUrlByPositionId($positionId) {
		return W_ABSPATH . 'building/' . $positionId . '/';
	}
	
	/**
	 * Возвращает $url для страницы строительства здания
	 * 
	 * @param int $buildingId
	 * @param int $positionId - позиция для строительства
	 */
	public static function getPlaceBuildingUrlToBeCreate($buildingId, $positionId) {
		return W_ABSPATH . 'building/create/' . $positionId . '/?ID_building=' . $buildingId;
	}
	
	/**
	 * Улучшает здание до определенного (но только следующего уровня)
	 * 
	 * @param int $levelToUpgrade
	 * 
	 * @throws Exception_Game
	 */
	public function upgradeToLevel($levelToUpgradeWant) {
		$realNextLevel = $this->getLevelToBuild();
		if ($levelToUpgradeWant != $realNextLevel) throw new Exception_Game_Level(); // Уровень апгрейда не совпадает с желаемым
		$this->isAvailableToUpgrade(true); // Выдаст исключения если невозможно построить
		/*
		// Проверяем, можно ли у здания произвести апгрейд до соответсвующего уровня
		$realNextLevel = $this->getLevelToBuild();
		if (!$realNextLevel) throw new Exception_Game_Level(Service_Translate::getText('Здание достигло максимального уровня'));
		if ($levelToUpgradeWant != $realNextLevel) throw new Exception_Game_Level(); // Уровень апгрейда не совпадает с желаемым
		
		//if (!$this->isAvailableToUpgrade()) return false; // Не хватает ресурсов или чего-то еще
		
		// Проверяем, не строится ли в данный момент это здание
		if (Game_Event_Building::isPlaceEventExist($this->positionId)) throw new Exception_Game_CurrentAction(Service_Translate::getText('Здание в данный момент строится'));
		
		$objGeneral = Game_General::instance();
		// Проверяем, не строится ли в данный момент что-то другое в месте
		if ($objGeneral->objPlace->hasBuildingEvents()) throw new Exception_Game_BusyWorkers(Service_Translate::getText('В данный момент проходит другое строительство'));
		
		// Проверяет, хватает ли ресурсов для апгрейда
		if (!$this->_isEnoughResources($levelToUpgradeWant)) throw new Exception_Game_Resources(); // Не хватает ресурсов
		*/
		
		// Если нет исключений, значит можно делать апгрейд
		return $this->_makeUpgradeToLevel($levelToUpgradeWant);
	}
	
	/**
	 * Выстраивает новое здание
	 */
	public function createBuilding() {
		$this->isAvailableToCreate(true); // Выдаст исключения если невозможно построить
		
		/*
		// Проверяем зависимости от зданий
		if (!$this->_isAvailableToCreateByAnotherBuildings()) throw new Exception_Game_BuildingsDependences();
		
		$objGeneral = Game_General::instance();
		if ($objGeneral->objPlace->getBuildingIdByPosition($this->positionId)) throw new Exception_Game_Exist(Service_Translate::getText('В этом месте уже построено другое здание'));
		
		// Проверяем, не исследуется ли в данный момент
		if (Game_Event_Building::isPlaceEventExist($this->positionId)) throw new Exception_Game_CurrentAction(Service_Translate::getText('Здание в данный момент строится'));
		
		// Проверяем, не строится ли в данный момент что-то другое в месте
		if ($objGeneral->objPlace->hasBuildingEvents()) throw new Exception_Game_BusyWorkers(Service_Translate::getText('В данный момент проходит другое строительство'));
		
		// Проверяем зависимости от ресурсов
		if (!$this->_isEnoughResources(1)) throw new Exception_Game_Resources(); // Не хватает ресурсов
		*/
		
		// Если нет исключений, значит можно осуществялть строительство
		return $this->_makeCreating();
	}
	
	/**
	 * Метод осуществляет строительство нового здания
	 *
	 */
	protected function _makeCreating() {
		// TODO по хорошему можно SQL транзакцию вставить, но не обязательно
		$objResourcesCost = $this->objBuilding->getBuildingLevelUpgradeCost(1);
		$objGeneral = Game_General::instance();
		$objGeneral->pay($objResourcesCost);
		$this->save();
		$this->_createBuildingEvent(1);
		return true;
	}
	
	/**
	 * Метод осуществляет усовершенствование здание до очередного уровня
	 * 
	 * @param int $levelToUpgradeWant
	 */
	protected function _makeUpgradeToLevel($levelToUpgradeWant) {
		// TODO по хорошему можно SQL транзакцию вставить, но не обязательно
		$objResourcesCost = $this->objBuilding->getBuildingLevelUpgradeCost($levelToUpgradeWant);
		$objGeneral = Game_General::instance();
		$objGeneral->pay($objResourcesCost);
		//$this->updateField('level', $levelToUpgradeWant);
		$this->_createBuildingEvent($levelToUpgradeWant);
		return true;
	}
	
	/**
	 * Получает время строительства здания
	 */
	public function getBuildingTime() {
		$buildingStartLevelTime = $this->objBuilding->getField('time_base');
		$buildingAdditionalLevelTime = $this->objBuilding->getField('time_additional');
		$buildingTimeCoef = $this->objBuilding->getField('time_coeff');
		$nextLevel = $this->getLevelToBuild();
		
		return Game_Building::getBuildingTime($buildingStartLevelTime, $buildingAdditionalLevelTime, $buildingTimeCoef, $nextLevel);
	}
	
	/**
	 * Получает стоимоть потребления нового уровня здания
	 * 
	 * @return array
	 */
	public function getConsumptionCost() {
		$nextLevel = $this->getLevelToBuild();
		$previosLevel = $nextLevel - 1;
		
		return Game_ResourceWithProduction::getBuildingLevelsConsumptionDifference($this->objBuilding->tableFields, $previosLevel, $nextLevel);
	}
	
	/**
	 * Создает событие "Строительство здания"
	 * 
	 * @param int $levelToBuild
	 */
	private function _createBuildingEvent($levelToBuild) {
		$objGeneral = Game_General::instance();
		$objGameEvent = new Game_Event(array(
			'ID_place' => $this->placeId,
			'ID_user' => $objGeneral->objUser->id,
			'event_type' => 'building',
			'ID_essence' => $this->positionId,
			'amount' => $levelToBuild,
			'event_time' => Service_Date::getTimestampToDBSaveWithSecondsMove($this->getBuildingTime())
		));
		$objGameEvent->save();
	}
	
	/**
	 * Проверяет, есть ли у этого здания события по строительству
	 * 
	 * @return boolean
	 */
	public function getBuildingEvents() {
		$objGeneral = Game_General::instance();
		$arrEvents = $objGeneral->objUser->getEventsData();
		$arrRet = array();
		foreach ($arrEvents as $arrEvent) {
			if ($arrEvent['ID_place'] == $this->placeId && $arrEvent['ID_essence'] == $this->positionId) $arrRet[] = $arrEvent;
		}
		return $arrRet;
	}

}
?>